#include <stdint.h>
#include "lite_mem_manager.h"

#ifndef NULL
    #define NULL 0
#endif

#define MEM_BLOCK_SIZE (4)
#define MEM_MAX_SIZE (4096)
#define MEM_ALLOC_TABLE_SIZE (MEM_MAX_SIZE / MEM_BLOCK_SIZE)

typedef struct
{
    void (*mem_init)(void);
    uint8_t* mem_pool_base;
    uint16_t* mem_map_base;
    uint32_t ready_flags;
} lite_mem_manager_t;

#if defined(_WIN32)
    uint8_t lite_mem_manager_pool[MEM_MAX_SIZE];
#else
    /*__align(4)*/ uint8_t lite_mem_manager_pool[MEM_MAX_SIZE];
#endif
uint16_t lite_mem_manager_map[MEM_ALLOC_TABLE_SIZE];

static lite_mem_manager_t lite_mem_manager =
{
    lite_mem_manager_init,
    lite_mem_manager_pool,
    lite_mem_manager_map,
    0,
};

static uint32_t mem_alloc(uint32_t size);
static uint8_t mem_free(uint32_t offset);

void lite_memcpy(void* des, void* src, uint32_t n)
{
    uint8_t* xdes = des;
    uint8_t* xsrc = src;
    while (n--)
    {
        *xdes++ = *xsrc++;
    }
}

void lite_memset(void* s, uint8_t c, uint32_t count)
{
    uint8_t* xs = s;
    while (count--)
    {
        *xs++ = c;
    }
}

void lite_mem_manager_init()
{
    lite_mem_manager.mem_init = lite_mem_manager_init;
    lite_mem_manager.mem_pool_base = lite_mem_manager_pool;
    lite_mem_manager.mem_map_base = lite_mem_manager_map;
    lite_memset(lite_mem_manager.mem_map_base, 0, MEM_ALLOC_TABLE_SIZE);
    lite_mem_manager.ready_flags = 1;
}

uint32_t lite_mem_used()
{
    uint32_t used = 0;
    uint32_t i;
    for (i = 0; i < MEM_ALLOC_TABLE_SIZE; i++)
    {
        if (lite_mem_manager.mem_map_base[i])
        {
            used++;
        }
    }
    return used;
}

static uint32_t mem_alloc(uint32_t size)
{
    signed long offset = 0;
    uint16_t nmemb;
    uint16_t cmemb = 0;
    uint32_t i;
    if (!lite_mem_manager.ready_flags)
    {
        lite_mem_manager.mem_init();
    }
    if (size == 0)
    {
        return 0XFFFFFFFF;
    }
    nmemb = size / MEM_BLOCK_SIZE;
    if (size % MEM_BLOCK_SIZE)
    {
        nmemb++;
    }
    for (offset = MEM_ALLOC_TABLE_SIZE - 1; offset >= 0; offset--)
    {
        if (!lite_mem_manager.mem_map_base[offset])
        {
            cmemb++;
        }
        else
        {
            cmemb = 0;
        }
        if (cmemb == nmemb)
        {
            for (i = 0; i < nmemb; i++)
            {
                lite_mem_manager.mem_map_base[offset + i] = nmemb;
            }
            return (offset * MEM_BLOCK_SIZE);
        }
    }
    return 0XFFFFFFFF;
}

static uint8_t mem_free(uint32_t offset)
{
    int i;
    if (!lite_mem_manager.ready_flags)
    {
        lite_mem_manager.mem_init();
        return 1;
    }
    if (offset < MEM_MAX_SIZE)
    {
        int index = offset / MEM_BLOCK_SIZE;
        int nmemb = lite_mem_manager.mem_map_base[index];
        for (i = 0; i < nmemb; i++)
        {
            lite_mem_manager.mem_map_base[index + i] = 0;
        }
        return 0;
    }
    else
    {
        return 2;
    }
}

void lite_free(void* ptr)
{
    uint32_t offset;
    if (ptr == NULL)
    {
        return;
    }
    offset = (uint32_t)ptr - (uint32_t)lite_mem_manager.mem_pool_base;
    mem_free(offset);
}

void* lite_malloc(uint32_t size)
{
    uint32_t offset;
    offset = mem_alloc(size);
    if (offset == 0XFFFFFFFF)
    {
        return NULL;
    }
    else
    {
        return (void*)((uint32_t)lite_mem_manager.mem_pool_base + offset);
    }
}

void* lite_realloc(void* ptr, uint32_t size)
{
    uint32_t offset;
    offset = mem_alloc(size);
    if (offset == 0XFFFFFFFF)
    {
        return NULL;
    }
    else
    {
        lite_memcpy((void*)((uint32_t)lite_mem_manager.mem_pool_base + offset), ptr, size);
        lite_free(ptr);
        return (void*)((uint32_t)lite_mem_manager.mem_pool_base + offset);
    }
}
